#include <QMainWindow>
#include <QFileDialog>
#include <QMessageBox>
#include <QList>
#include <QMetaObject>
#include <QStatusBar>
#include <QDebug>
#include <QSerialPortInfo>
#include <QRegularExpression>
#include "comm_utils.h"
#include "ev_comm.h"
#include "task_firm_upgrade.h"
#include "task_dev_reset.h"
#include "task_firm_read.h"
#include "task_read_version.h"
#include "task_send_command.h"
#include "ev_cmd_type.h"

EvComm::EvComm(MainWindow *window, QObject *parent):
    QObject(parent), m_window(window)
{
    threadPool = new QThreadPool(this);
    threadPool->setMaxThreadCount(4);
    isProcessing = false;
    m_serial = new EvSerial(this);

    /* update serial baudrate combo box */
    m_window->baudRateComboBox->clear();
    QList<qint32> baudRates = {9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600};
    for (auto it = baudRates.constBegin(); it != baudRates.constEnd(); ++it) {
        m_window->baudRateComboBox->addItem(QString::number(*it), *it);
    }
    int defaultIndex = m_window->baudRateComboBox->findText("115200");
    if (defaultIndex != -1) {
        m_window->baudRateComboBox->setCurrentIndex(defaultIndex);
    }

    connect(m_window->upgradeFirmBtn, &QPushButton::clicked, this, &EvComm::onFirmwareUpgrade);
    connect(m_window->resetBtn, &QPushButton::clicked, this, &EvComm::onDevReset);
    connect(m_window->readFirmBtn, &QPushButton::clicked, this, &EvComm::onFirmwareRead);
    connect(m_window->readVersionBtn, &QPushButton::clicked, this, &EvComm::onReadVersion);
    connect(m_window->sendCommandBtn, &QPushButton::clicked, this, &EvComm::onSendCommand);
    connect(m_window->opSerialPortBtn, &QPushButton::clicked, this, &EvComm::onOpSerialPort);
    connect(m_window->refreshSerialPortBtn, &QPushButton::clicked, this, &EvComm::onRefreshSerialPort);
    connect(m_serial, &EvSerial::logReceived, this,
        [this](std::shared_ptr<ev_packet_t> log) {
            QString content;
            if(log->cmd == EV_NOTIFY_DEVICE_LOG && log->len > 0) {
                QByteArray byteArray = QByteArray::fromRawData((const char *)log->data, log->len);
                content = LogUtils::makeLogStr(QString::fromUtf8(byteArray));
            } else {
                content = LogUtils::makeLogBin("", log.get());
            }
            this->m_window->deviceLogDisplay->append(content);
    });
}

EvComm::~EvComm()
{
}

void EvComm::onFirmwareUpgrade()
{
    QString fileName = QFileDialog::getOpenFileName(m_window,
                                                    "选择固件文件",
                                                    "",
                                                    "固件文件 (*.bin *.hex);;所有文件 (*.*)");
    if (!fileName.isEmpty()) {
        onCommButtonHandler<FirmwareUpgradeTask>(m_serial, fileName);
    }
}

void EvComm::onDevReset()
{
    onCommButtonHandler<DevResetTask>(m_serial, 500);
}

void EvComm::onFirmwareRead()
{
    QString fileName = QFileDialog::getSaveFileName(m_window,
                                                    "保存固件文件",
                                                    QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss"),
                                                    "固件文件 (*.bin);;所有文件 (*.*)");
    if (!fileName.isEmpty()) {
        onCommButtonHandler<FirmwareReadTask>(m_serial, fileName);
    }
}

void EvComm::onReadVersion()
{
    onCommButtonHandler<ReadVersionTask>(m_serial, 0);
}


static bool parseCommand(const QString& input, int& cmd, QByteArray& data)
{
    QString cleaned = input;
    cleaned.remove(" ");

    static const QRegularExpression regex(R"(^([0-9A-Fa-f]{1,4}),\[([0-9A-Fa-f,]*)\]$)");
    QRegularExpressionMatch match = regex.match(cleaned);

    if (!match.hasMatch()) {
        return false;
    }

    QString cmdStr = match.captured(1);
    bool ok;
    cmd = cmdStr.toInt(&ok, 16);
    if (!ok || cmd < 0 || cmd > 0xFFFF) {
        return false;
    }

    QString dataStr = match.captured(2);
    data.clear();

    if (!dataStr.isEmpty()) {
        QStringList hexList = dataStr.split(',', Qt::SkipEmptyParts);

        for (int i = 0; i < hexList.size(); ++i) {
            const QString& hex = hexList.at(i);
            if (hex.length() != 2) {
                return false;
            }
            int byte = hex.toInt(&ok, 16);
            if (!ok || byte < 0 || byte > 255) {
                return false;
            }
            data.append(static_cast<char>(byte));
        }
    }

    return true;
}

void EvComm::onSendCommand()
{
    QString input = m_window->commandInput->text();
    int cmd = -1;
    QByteArray data;
    if (!parseCommand(input, cmd, data)) {
        QMessageBox::warning(m_window, "错误",
            "无效的输入!!!!\n"
            "输入格式: 命令, [数据]\n"
            "参考输入: 20A4, [A0, 02, 00, 00]"
        );
        return;
    }
    onCommButtonHandler<SendCommandTask>(m_serial, cmd, data);
}

void EvComm::onOpSerialPort()
{
    QString portName = m_window->serialPortComboBox->currentText();
    if (portName.isEmpty()) {
        QMessageBox::warning(m_window, "错误", "未选择串口!!!");
        return;
    }

    QString result_mssg;
    if (m_serial->isOpen()) {
        m_serial->close();
        m_window->opSerialPortBtn->setText("打开");
        m_window->serialPortComboBox->setEnabled(true);
        m_window->baudRateComboBox->setEnabled(true);
        m_window->refreshSerialPortBtn->setEnabled(true);
        result_mssg = "关闭串口成功";
    } else {
        bool open_result = m_serial->open(portName, m_window->baudRateComboBox->currentText().toInt());
        if (open_result) {
            m_window->opSerialPortBtn->setText("关闭");
            m_window->serialPortComboBox->setEnabled(false);
            m_window->baudRateComboBox->setEnabled(false);
            m_window->refreshSerialPortBtn->setEnabled(false);
            result_mssg = "打开串口成功";
        } else {
            result_mssg = "打开串口失败";
        }
    }
    m_window->commandDisplay->append(LogUtils::makeLogBin(result_mssg));
    m_window->statusBar()->showMessage(result_mssg);
}

void EvComm::onRefreshSerialPort()
{
    m_window->serialPortComboBox->clear();
    QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
    for (auto it = ports.constBegin(); it != ports.constEnd(); ++it) {
        m_window->serialPortComboBox->addItem(it->portName());
    }
}
